package com.spring.theory.bean.beanPostProcessor;

import static com.spring.theory.constant.GeneralConstant.LOG_INFO;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.MethodParameter;
import org.springframework.core.env.StandardEnvironment;

/**
 * 阅读 2-2
 * BeanPostProcessor bean后处理器
 * - 针对bean生命周期的各个阶段提供扩展
 * Autowired bean后置处理器使用
 *
 * @author H
 * @version 1.0
 * @date 2023-12-25 10:56
 */
@Slf4j
public class TestBeanPostProcessor {

    public static void main(String[] args) {
        //generalBeanPostProcessor();
        autowiredPostProcessor();
    }

    /**
     * autoWired注解的解析
     */
    @SneakyThrows
    private static void autowiredPostProcessor() {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //注册成品bean，不会进行bean的创建、依赖注入、初始化等操作
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        // @Value 解析器
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // {} 的解析器
        beanFactory.addEmbeddedValueResolver(text -> new StandardEnvironment().resolvePlaceholders(text));

        /**
         * 1. autowired注解的解析器
         * - 实现InstantiationAwareBeanPostProcessor接口
         * - 需要beanFactory参数，以此来找寻其它需要依赖注入的bean（如bean1里面需要注入bean2、bean3）
         */
        AutowiredAnnotationBeanPostProcessor postProcessor = new AutowiredAnnotationBeanPostProcessor();
        postProcessor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        log.info("DI操作前===bean1:{}", bean1);

        /**
         * 2. bean实际赋值步骤；
         * - 参数1表示是否手动指定bean的值注入；null-标识从beanFactory里面去根据类型匹配注入属性
         */
        postProcessor.postProcessProperties(null, bean1, "bean1");
        log.info("DI操作后===bean1:{}", bean1);

        log.info(LOG_INFO + "postProcessProperties内部操作大致流程");
        testPostProcessorProperties(beanFactory, postProcessor, bean1);

    }

    private static void testPostProcessorProperties(DefaultListableBeanFactory beanFactory, AutowiredAnnotationBeanPostProcessor postProcessor, Bean1 bean1) throws Throwable {
        Method findAutowiringMetadataMethod = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
        findAutowiringMetadataMethod.setAccessible(true);
        /**
         * 1. 找到Bean1内标注@Autowired的属性；方法反射调用得到注入信息
         */
        InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadataMethod.invoke(postProcessor, "bean1", Bean1.class, null);
        /**
         * 2. 注入属性值；找到Bean1中有autowaire注解的字段，并且反射注入值
         */
        metadata.inject(bean1, "bean1", null);
        log.info("bean1:{}", bean1);

        log.info(LOG_INFO + "metadata.inject内部操作大致流程");
        metadataInject(beanFactory);

    }

    /**
     * 找到带有@Autowired注解的属性值；后面再通过反射注入这些属性值
     */
    private static void metadataInject(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException, NoSuchMethodException {
        /**
         * 普通bean的查找
         */
        Field bean3 = Bean1.class.getDeclaredField("bean3");
        DependencyDescriptor dd1 = new DependencyDescriptor(bean3, true);
        // 根据成员变量的类型在容器中找到对应的bean
        Object o1 = beanFactory.doResolveDependency(dd1, null, null, null);
        log.info("metadataInject bean3对象值注入：{}", o1);

        /**
         * 方法属性参数的查找
         */
        Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
        //MethodParameter注入参数，标识第几个参数
        DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
        //根据方法参数的类型在容器中找到对应的bean
        Object o2 = beanFactory.doResolveDependency(dd2, null, null, null);
        log.info("metadataInject bean2方法值注入：{}", o2);

        /**
         * 值注入的查找
         */
        Method setValue = Bean1.class.getDeclaredMethod("setValue", String.class);
        DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setValue, 0), true);
        Object o3 = beanFactory.doResolveDependency(dd3, null, null, null);
        log.info("metadataInject home对象值注入：{}", o3);

    }

    /**
     * Bean后处理器的解析
     */
    private static void generalBeanPostProcessor() {
        /**
         * GenericApplicationContext是一个没有任何解析器bean的容器；干净的容器
         */
        GenericApplicationContext context = new GenericApplicationContext();
        //手动注册bean对象
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);
        context.registerBean("bean4", Bean4.class);

        /**
         * 1. 对于 值注入 的获取需要此类解析器
         */
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());

        /**
         * 2. @AutoWired @Value 解析；主要在DI阶段
         */
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
        /**
         * 3. @Resource @PostConstruct @PreDestroy解析；
         * - CommonAnnotationBeanPostProcessor优先级高于autowired的，order接口决定的
         */
        context.registerBean(CommonAnnotationBeanPostProcessor.class);

        /**
         * 4. @ConfigurationProperties 数据绑定解析；Bean初始化前执行绑定
         */
        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

        /**
         * 5. 初始化容器; 执行beanFactory后处理器，添加bean后处理器，初始化所有单例
         */
        context.refresh();
        log.info("@ConfigurationProperties属性绑定bean4:{}", context.getBean(Bean4.class));
        context.close();

    }

    static class Bean1 {

        public Bean1() {
            log.info("bean1的构造器方法");
        }

        private Bean2 bean2;

        @Autowired
        public void setBean2(Bean2 bean2) {
            log.info("DI操作==bean1内@Autowired bean2生效：{}", bean2);
            this.bean2 = bean2;
        }

        //@Autowired
        private Bean3 bean3;

        /**
         * @Autowired（默认按属性名称注入）、@Resource（先按属性名称注入，失败就按类型注入） - 放在属性上，是对属性的值注入
         * - 放在方法上，是对方法参数的值注入
         * byName：根据属性名称注入，注入值 bean 的 id 值和类属性名称一样，且数据类型也一致
         * byType：根据属性类型注入，java类中引用类型的数据类型和spring容器中（配置文件）bean的class属性是同源关系（同一个|父子类|接口实现类），这样的bean能够赋值给引用类型
         */
        @Resource
        public void setBean3(Bean3 bean3) {
            log.info("DI操作==bean1内@Resource bean3生效：{}", bean3);
            this.bean3 = bean3;
        }

        private String value;

        @Autowired
        public void setValue(@Value("${JAVA_HOME}") String value) {
            log.info("DI操作==bean1内@Autowired加@Value生效：{}", value);
            this.value = value;
        }

        @PostConstruct
        public void init() {
            log.info("Bean1的@PostConstruct初始化生效");
        }

        @PreDestroy
        public void destroy() {
            log.info("Bean1的@PreDestroy销毁生效");
        }

        @Override
        public String toString() {
            return "Bean1{" +
                    "bean2=" + bean2 +
                    ", bean3=" + bean3 +
                    ", value='" + value + '\'' +
                    '}';
        }
    }

    @Data
    static class Bean2 {
    }

    @Data
    static class Bean3 {
        private String name = "哈哈哈";
    }

    /**
     * 根据键值对绑定配置文件的信息
     */
    @ConfigurationProperties(prefix = "java")
    @Data
    static class Bean4 {

        private String home;
        private String version;

    }
}
